package callcenter

import (
	"errors"
	"fmt"
	"gitee.com/zackeus/go-boot/freeswitch/mod/callcenter/agent"
	"gitee.com/zackeus/go-boot/freeswitch/mod/callcenter/queue"
	"gitee.com/zackeus/go-boot/freeswitch/mod/xmlcurl"
	"github.com/beevik/etree"
	"strconv"
)

type (
	XmlQueue struct {
		doc  *etree.Document
		body *etree.Element
	}

	QueueOption func(xml *XmlQueue)
)

// NewQueue callcenter queue xml
// strategy: 分配策略
func NewQueue(name string, strategy queue.Strategy, opts ...QueueOption) (xmlcurl.XmlConf, error) {
	xml := &XmlQueue{
		doc: etree.NewDocument(),
	}
	/* 属性值不转译 */
	xml.doc.WriteSettings.CanonicalAttrVal = true
	xml.doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`)
	document := xml.doc.CreateElement("document")
	document.CreateAttr("type", "freeswitch/xml")

	section := document.CreateElement("section")
	section.CreateAttr("name", "configuration")

	configuration := section.CreateElement("configuration")
	configuration.CreateAttr("name", "callcenter.conf")

	queues := configuration.CreateElement("queues")

	queueElement := queues.CreateElement("queue")
	queueElement.CreateAttr("name", name)
	xml.body = queueElement

	xml.createParam("strategy", string(strategy))
	xml.createParam("moh-sound", "$${hold_music}")
	xml.createParam("time-base-score", string(queue.ScoreQueue))

	for _, opt := range opts {
		opt(xml)
	}
	return xml, nil
}

func (x *XmlQueue) createParam(name string, value string) {
	param := x.body.FindElement(fmt.Sprintf("./param[@name='%s']", name))
	if param == nil {
		param = x.body.CreateElement("param")
	}

	param.CreateAttr("name", name)
	param.CreateAttr("value", value)
}

func (x *XmlQueue) removeParam(name string) {
	param := x.body.FindElement(fmt.Sprintf("./param[@name='%s']", name))
	if param != nil {
		x.body.RemoveChild(param)
	}
}

// WithMohSound 排队语音
func WithMohSound(s string) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("moh-sound", s)
	}
}

// WithTimeBaseScore 积分选项
func WithTimeBaseScore(s queue.Score) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("time-base-score", string(s))
	}
}

// WithAbandonedResumeAllowed 保留队列位置(根据caller_id_number恢复之前的排队信息)
// 如果上一次加入队列，未被坐席接听就挂断了，再次呼叫时可以恢复上次的排队信息，被更优先的分配到坐席。
// discard-abandoned-after 之前的信息是不能恢复的
func WithAbandonedResumeAllowed(b bool) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("abandoned-resume-allowed", strconv.FormatBool(b))
	}
}

// WithDiscardAbandonedAfter 队列保留时长(未被坐席接通的排队信息保存时间，默认值60秒)
func WithDiscardAbandonedAfter(t uint64) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("discard-abandoned-after", strconv.FormatUint(t, 10))
	}
}

// WithMaxWaitTime 最大排队时间，单位秒。默认值0，就是禁用这个设置。例如，超过3分钟没坐席接听可以转到IVR或者语音信箱
func WithMaxWaitTime(t uint64) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("max-wait-time", strconv.FormatUint(t, 10))
	}
}

// WithMaxWaitTimeWithNoAgent 队列为空时的最大等待时间。单位秒，默认值0，就是禁用这个设置
// 什么是队列为空：队列没有坐席状态(Agent Status)是 Available 、 Available (On Demand) 、 On Break 的坐席
func WithMaxWaitTimeWithNoAgent(t uint64) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("max-wait-time-with-no-agent", strconv.FormatUint(t, 10))
	}
}

// WithMaxReachedTimeWithNoAgent 队列恢复时长
// 当因max-wait-time-with-no-agent 超时而退出时，多少时间内拒绝新的来电
// max-wait-time-with-no-agent不为0时这个值才有效，最小的等待时间为max-wait-time-with-no-agent+max-wait-time-with-no-agent-time-reached，默认值5。设置0就是禁用这个设置
// 为了解决加入队列时，队列就为空时queue->last_agent_exist为0，queue->last_agent_exist_check -
// queue->last_agent_exist无法计算队列空闲时间，进行第二次判段， queue->last_agent_exist_check -
// m->t_member_called 的结果是加入队列总共时间，
// 当加入队列总时间大于max-wait-time-with-no-agent+max-wait-time-with-no-agent-time-reached，就离开队列
func WithMaxReachedTimeWithNoAgent(t uint64) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("max-wait-time-with-no-agent-time-reached", strconv.FormatUint(t, 10))
	}
}

// WithTierRulesApply 是否启动tier规则，默认值False。启动等级规则后，需要等待一定时间后，才会分配高等级的坐席
func WithTierRulesApply(b bool) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("tier-rules-apply", strconv.FormatBool(b))
	}
}

// WithTierRuleWaitSecond 梯队等待时长
func WithTierRuleWaitSecond(t uint64) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("tier-rule-wait-second", strconv.FormatUint(t, 10))
	}
}

// WithTierRuleWaitMultiplyLevel 启用梯队级别
// True: 等待时间大于 tier-rule-wait-second * tier.level 才会分配下一个等级的坐席。
// False: 等待时间大于 tier-rule-wait-second 才会分配下一个等级的坐席
func WithTierRuleWaitMultiplyLevel(b bool) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("tier-rule-wait-multiply-level", strconv.FormatBool(b))
	}
}

// WithTierRuleNoAgentNoWait 跳过无坐席的技能等级
func WithTierRuleNoAgentNoWait(b bool) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("tier-rule-no-agent-no-wait", strconv.FormatBool(b))
	}
}

// WithAgentNoAnswerStatus 无应答状态
//
//	如果坐席不应答次数超过max-no-answer(Agent中设置)，则设置坐席的状态为 agent-no-answer-status 的值。默认值 On Break
func WithAgentNoAnswerStatus(s agent.Status) QueueOption {
	return func(x *XmlQueue) {
		x.createParam("agent-no-answer-status", string(s))
	}
}

func (x *XmlQueue) BodyElement() *etree.Element {
	return x.body
}

func (x *XmlQueue) MarshalToXml() ([]byte, error) {
	x.doc.Indent(1)
	return x.doc.WriteToBytes()
}

func (x *XmlQueue) UnMarshalFromString(v string) error {
	if x.doc == nil {
		x.doc = etree.NewDocument()
	}
	if err := x.doc.ReadFromString(v); err != nil {
		return err
	}

	body := x.doc.FindElement("./include/queue")
	if body == nil {
		return errors.New("the element include/queue is nil")
	}
	x.body = body
	return nil
}

func (x *XmlQueue) WriteToFile(path string) error {
	x.doc.Indent(1)
	return x.doc.WriteToFile(path)
}
